From e098be34aa96bbcddb786a09b13da877d2d709ad Mon Sep 17 00:00:00 2001
From: Andriy Grytsenko <andrej@rep.kiev.ua>
Date: Tue, 6 Nov 2012 05:49:19 +0200
Subject: [PATCH 03/22] [#3584608]Fix for rarely broken DnD due to FmDndDest
 source caching.

The previous behavior was to cache data and reset cache when drag leaves
widget. This is completely wrong - we never get informed if we refuse drop
(for example, on wrong target) therefore drag source kept the same while
it was already changed. The only valid behavior is to keep drag context
referenced and compare it with one we get in handler and if it's changed
then reset our cache. This way we don't have to requery data again when
pointer leaves widget but still inside of application window - the drag
context is still the same, i.e. we get some optimization as a side effect.
---
 src/gtk/fm-dnd-dest.c | 44 ++++++++++++++------------------------------
 1 file changed, 14 insertions(+), 30 deletions(-)

diff --git a/src/gtk/fm-dnd-dest.c b/src/gtk/fm-dnd-dest.c
index 37686ce..21cf2c0 100644
--- a/src/gtk/fm-dnd-dest.c
+++ b/src/gtk/fm-dnd-dest.c
@@ -108,10 +108,10 @@ struct _FmDndDest
 
     int info_type; /* type of src_files */
     FmPathList* src_files;
+    GdkDragContext* context;
     guint32 src_dev; /* UNIX dev of source fs */
     const char* src_fs_id; /* filesystem id of source fs */
     FmFileInfo* dest_file;
-    guint idle; /* idle handler */
 
     gboolean waiting_data;
     gboolean has_handlers;
@@ -211,11 +211,6 @@ static void fm_dnd_dest_dispose(GObject *object)
 
     fm_dnd_dest_set_widget(dd, NULL);
 
-    if(dd->idle)
-    {
-        g_source_remove(dd->idle);
-        dd->idle = 0;
-    }
     clear_src_cache(dd);
 
     G_OBJECT_CLASS(fm_dnd_dest_parent_class)->dispose(object);
@@ -388,6 +383,11 @@ static gboolean fm_dnd_dest_files_dropped(FmDndDest* dd, int x, int y,
 static void clear_src_cache(FmDndDest* dd)
 {
     /* free cached source files */
+    if(dd->context)
+    {
+        g_object_unref(dd->context);
+        dd->context = NULL;
+    }
     if(dd->src_files)
     {
         fm_path_list_unref(dd->src_files);
@@ -405,16 +405,6 @@ static void clear_src_cache(FmDndDest* dd)
     dd->waiting_data = FALSE;
 }
 
-static gboolean clear_src_cache_on_idle(gpointer user_data)
-{
-    GDK_THREADS_ENTER();
-    /* check if dd is still valid */
-    if(!g_source_is_destroyed(g_main_current_source()))
-        clear_src_cache((FmDndDest*)user_data);
-    GDK_THREADS_LEAVE();
-    return FALSE;
-}
-
 #if 0
 /* the returned list can be either FmPathList or FmFileInfoList */
 /* check with fm_list_is_path_list() and fm_list_is_file_info_list(). */
@@ -581,6 +571,10 @@ gboolean _on_drag_data_received(FmDndDest* dd, GdkDragContext *drag_context,
     dd->src_files = files;
     dd->waiting_data = FALSE;
     dd->info_type = info;
+    /* keep context to verify if it's changed */
+    if(G_UNLIKELY(dd->context))
+        g_object_unref(dd->context);
+    dd->context = g_object_ref(drag_context);
     return (files != NULL);
 }
 
@@ -718,7 +712,7 @@ gboolean _on_drag_drop(FmDndDest* dd, GdkDragContext *drag_context,
         }
 
         /* see if the dragged files are cached by "drag-motion" handler */
-        if(dd->src_files)
+        if(dd->src_files && drag_context == dd->context)
         {
             GdkDragAction action = gdk_drag_context_get_selected_action(drag_context);
             /* emit files-dropped signal */
@@ -777,14 +771,6 @@ GdkDragAction fm_dnd_dest_get_default_action(FmDndDest* dd,
         /* query drag sources in any case */
         goto query_sources;
 
-    /* we may have another data already so clear the cache */
-    if(dd->idle)
-    {
-        g_source_remove(dd->idle);
-        dd->idle = 0;
-        clear_src_cache(dd);
-    }
-
     /* special support for dropping onto desktop entry */
     if(fm_file_info_is_desktop_entry(dest))
     {
@@ -802,8 +788,10 @@ GdkDragAction fm_dnd_dest_get_default_action(FmDndDest* dd,
     if(target == dest_target_atom[FM_DND_DEST_TARGET_XDS])
         return GDK_ACTION_COPY;
 
-    if(!dd->src_files)  /* we didn't have any data, cache it */
+    /* we have no valid data, query it now */
+    if(!dd->src_files || dd->context != drag_context)
     {
+        clear_src_cache(dd);
 query_sources:
         action = 0;
         if(!dd->waiting_data) /* we're still waiting for "drag-data-received" signal */
@@ -886,13 +874,9 @@ query_sources:
  */
 void fm_dnd_dest_drag_leave(FmDndDest* dd, GdkDragContext* drag_context, guint time)
 {
-    if(dd->idle == 0)
-        dd->idle = g_idle_add_full(G_PRIORITY_LOW, clear_src_cache_on_idle, dd, NULL);
 }
 
 static void on_drag_leave(GtkWidget *widget, GdkDragContext *drag_context,
                           guint time, FmDndDest* dd)
 {
-    if(dd->idle == 0)
-        dd->idle = g_idle_add_full(G_PRIORITY_LOW, clear_src_cache_on_idle, dd, NULL);
 }
-- 
1.8.0.1

